home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / EXAMPLES / halomagic.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  30.0 KB  |  1,095 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
  3.  
  4. /* This program is freely distributable without licensing fees 
  5.    and is provided without guarantee or warrantee expressed or 
  6.    implied. This program is -not- in the public domain. */
  7.  
  8. /* dinoshade.c with an added "magic halo" effect when you hit the
  9.    space bar.  You can use an overlaid or blended halo (blending is
  10.    slower). */
  11.  
  12. /* Example for PC game developers to show how to *combine* texturing,
  13.    reflections, and projected shadows all in real-time with OpenGL.
  14.    Robust reflections use stenciling.  Robust projected shadows
  15.    use both stenciling and polygon offset.  PC game programmers
  16.    should realize that neither stenciling nor polygon offset are 
  17.    supported by Direct3D, so these real-time rendering algorithms
  18.    are only really viable with OpenGL. 
  19.    
  20.    The program has modes for disabling the stenciling and polygon
  21.    offset uses.  It is worth running this example with these features
  22.    toggled off so you can see the sort of artifacts that result.
  23.    
  24.    Notice that the floor texturing, reflections, and shadowing
  25.    all co-exist properly. */
  26.  
  27. /* When you run this program:  Left mouse button controls the
  28.    view.  Middle mouse button controls light position (left &
  29.    right rotates light around dino; up & down moves light
  30.    position up and down).  Right mouse button pops up menu. */
  31.  
  32. /* Check out the comments in the "redraw" routine to see how the
  33.    reflection blending and surface stenciling is done.  You can
  34.    also see in "redraw" how the projected shadows are rendered,
  35.    including the use of stenciling and polygon offset. */
  36.  
  37. /* This program is derived from glutdino.c */
  38.  
  39. /* Compile: cc -o halomagic halomagic.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
  40.  
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <math.h>       /* for cos(), sin(), and sqrt() */
  45. #include <GL/glut.h>    /* OpenGL Utility Toolkit header */
  46.  
  47. /* Some <math.h> files do not define M_PI... */
  48. #ifndef M_PI
  49. #define M_PI 3.14159265358979323846
  50. #endif
  51.  
  52. /* Variable controlling various rendering modes. */
  53. static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
  54. static int renderShadow = 0, renderDinosaur = 1, renderReflection = 0;
  55. static int linearFiltering = 0, useMipmaps = 0, useTexture = 0;
  56. static int reportSpeed = 0;
  57. static int animation = 0;
  58. static GLboolean lightSwitch = GL_TRUE;
  59. static int directionalLight = 1;
  60. static int forceExtension = 0;
  61. static int haloMagic = 0, blendedHalo = 0;
  62. static GLfloat haloScale = 1.0, haloTime = 0.0;
  63.  
  64. /* Time varying or user-controled variables. */
  65. static float jump = 0.0;
  66. static float lightAngle = 0.0, lightHeight = 20;
  67. GLfloat angle = -150;   /* in degrees */
  68. GLfloat angle2 = 30;   /* in degrees */
  69.  
  70. int moving, startx, starty;
  71. int lightMoving = 0, lightStartX, lightStartY;
  72.  
  73. enum {
  74.   MISSING, EXTENSION, ONE_DOT_ONE
  75. };
  76. int polygonOffsetVersion;
  77.  
  78. static GLdouble bodyWidth = 3.0;
  79. /* *INDENT-OFF* */
  80. static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
  81.   {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
  82.   {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
  83.   {1, 2} };
  84. static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
  85.   {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
  86.   {13, 9}, {11, 11}, {9, 11} };
  87. static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
  88.   {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
  89. static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
  90.   {9.6, 15.25}, {9, 15.25} };
  91. static GLfloat lightPosition[4];
  92. static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
  93. static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
  94. /* *INDENT-ON* */
  95.  
  96. /* Nice floor texture tiling pattern. */
  97. static char *circles[] = {
  98.   "....xxxx........",
  99.   "..xxxxxxxx......",
  100.   ".xxxxxxxxxx.....",
  101.   ".xxx....xxx.....",
  102.   "xxx......xxx....",
  103.   "xxx......xxx....",
  104.   "xxx......xxx....",
  105.   "xxx......xxx....",
  106.   ".xxx....xxx.....",
  107.   ".xxxxxxxxxx.....",
  108.   "..xxxxxxxx......",
  109.   "....xxxx........",
  110.   "................",
  111.   "................",
  112.   "................",
  113.   "................",
  114. };
  115.  
  116. static void
  117. makeFloorTexture(void)
  118. {
  119.   GLubyte floorTexture[16][16][3];
  120.   GLubyte *loc;
  121.   int s, t;
  122.  
  123.   /* Setup RGB image for the texture. */
  124.   loc = (GLubyte*) floorTexture;
  125.   for (t = 0; t < 16; t++) {
  126.     for (s = 0; s < 16; s++) {
  127.       if (circles[t][s] == 'x') {
  128.     /* Nice blue. */
  129.         loc[0] = 0x1f;
  130.         loc[1] = 0x1f;
  131.         loc[2] = 0x8f;
  132.       } else {
  133.     /* Light gray. */
  134.         loc[0] = 0xca;
  135.         loc[1] = 0xca;
  136.         loc[2] = 0xca;
  137.       }
  138.       loc += 3;
  139.     }
  140.   }
  141.  
  142.   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  143.  
  144.   if (useMipmaps) {
  145.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  146.       GL_LINEAR_MIPMAP_LINEAR);
  147.     gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
  148.       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
  149.   } else {
  150.     if (linearFiltering) {
  151.       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  152.     } else {
  153.       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  154.     }
  155.     glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
  156.       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
  157.   }
  158. }
  159.  
  160. enum {
  161.   X, Y, Z, W
  162. };
  163. enum {
  164.   A, B, C, D
  165. };
  166.  
  167. /* Create a matrix that will project the desired shadow. */
  168. void
  169. shadowMatrix(GLfloat shadowMat[4][4],
  170.   GLfloat groundplane[4],
  171.   GLfloat lightpos[4])
  172. {
  173.   GLfloat dot;
  174.  
  175.   /* Find dot product between light position vector and ground plane normal. */
  176.   dot = groundplane[X] * lightpos[X] +
  177.     groundplane[Y] * lightpos[Y] +
  178.     groundplane[Z] * lightpos[Z] +
  179.     groundplane[W] * lightpos[W];
  180.  
  181.   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
  182.   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
  183.   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
  184.   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
  185.  
  186.   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
  187.   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
  188.   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
  189.   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
  190.  
  191.   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
  192.   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
  193.   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
  194.   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
  195.  
  196.   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
  197.   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
  198.   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
  199.   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
  200.  
  201. }
  202.  
  203. /* Find the plane equation given 3 points. */
  204. void
  205. findPlane(GLfloat plane[4],
  206.   GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
  207. {
  208.   GLfloat vec0[3], vec1[3];
  209.  
  210.   /* Need 2 vectors to find cross product. */
  211.   vec0[X] = v1[X] - v0[X];
  212.   vec0[Y] = v1[Y] - v0[Y];
  213.   vec0[Z] = v1[Z] - v0[Z];
  214.  
  215.   vec1[X] = v2[X] - v0[X];
  216.   vec1[Y] = v2[Y] - v0[Y];
  217.   vec1[Z] = v2[Z] - v0[Z];
  218.  
  219.   /* find cross product to get A, B, and C of plane equation */
  220.   plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
  221.   plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
  222.   plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
  223.  
  224.   plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
  225. }
  226.  
  227. void
  228. extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
  229.   GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
  230. {
  231.   static GLUtriangulatorObj *tobj = NULL;
  232.   GLdouble vertex[3], dx, dy, len;
  233.   int i;
  234.   int count = dataSize / (int) (2 * sizeof(GLfloat));
  235.  
  236.   if (tobj == NULL) {
  237.     tobj = gluNewTess();  /* create and initialize a GLU
  238.                              polygon tesselation object */
  239.     gluTessCallback(tobj, GLU_BEGIN, glBegin);
  240.     gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
  241.     gluTessCallback(tobj, GLU_END, glEnd);
  242.   }
  243.   glNewList(side, GL_COMPILE);
  244.   glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
  245.                                tessellation */
  246.   gluBeginPolygon(tobj);
  247.   for (i = 0; i < count; i++) {
  248.     vertex[0] = data[i][0];
  249.     vertex[1] = data[i][1];
  250.     vertex[2] = 0;
  251.     gluTessVertex(tobj, vertex, data[i]);
  252.   }
  253.   gluEndPolygon(tobj);
  254.   glEndList();
  255.   glNewList(edge, GL_COMPILE);
  256.   glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
  257.                              from being "smoothed" */
  258.   glBegin(GL_QUAD_STRIP);
  259.   for (i = 0; i <= count; i++) {
  260.     /* mod function handles closing the edge */
  261.     glVertex3f(data[i % count][0], data[i % count][1], 0.0);
  262.     glVertex3f(data[i % count][0], data[i % count][1], thickness);
  263.     /* Calculate a unit normal by dividing by Euclidean
  264.        distance. We * could be lazy and use
  265.        glEnable(GL_NORMALIZE) so we could pass in * arbitrary
  266.        normals for a very slight performance hit. */
  267.     dx = data[(i + 1) % count][1] - data[i % count][1];
  268.     dy = data[i % count][0] - data[(i + 1) % count][0];
  269.     len = sqrt(dx * dx + dy * dy);
  270.     glNormal3f(dx / len, dy / len, 0.0);
  271.   }
  272.   glEnd();
  273.   glEndList();
  274.   glNewList(whole, GL_COMPILE);
  275.   glFrontFace(GL_CW);
  276.   glCallList(edge);
  277.   glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
  278.   glCallList(side);
  279.   glPushMatrix();
  280.   glTranslatef(0.0, 0.0, thickness);
  281.   glFrontFace(GL_CCW);
  282.   glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
  283.   glCallList(side);
  284.   glPopMatrix();
  285.   glEndList();
  286. }
  287.  
  288. /* Enumerants for refering to display lists. */
  289. typedef enum {
  290.   RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
  291.   LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
  292. } displayLists;
  293.  
  294. static void
  295. makeDinosaur(void)
  296. {
  297.   extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
  298.     BODY_SIDE, BODY_EDGE, BODY_WHOLE);
  299.   extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
  300.     ARM_SIDE, ARM_EDGE, ARM_WHOLE);
  301.   extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
  302.     LEG_SIDE, LEG_EDGE, LEG_WHOLE);
  303.   extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
  304.     EYE_SIDE, EYE_EDGE, EYE_WHOLE);
  305. }
  306.  
  307. static void
  308. drawDinosaur(void)
  309.  
  310. {
  311.   glPushMatrix();
  312.   /* Translate the dinosaur to be at (0,8,0). */
  313.   glTranslatef(-8, -8, -bodyWidth / 2);
  314.   glTranslatef(0.0, jump, 0.0);
  315.   glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
  316.   glCallList(BODY_WHOLE);
  317.   glTranslatef(0.0, 0.0, bodyWidth);
  318.   glCallList(ARM_WHOLE);
  319.   glCallList(LEG_WHOLE);
  320.   glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
  321.   glCallList(ARM_WHOLE);
  322.   glTranslatef(0.0, 0.0, -bodyWidth / 4);
  323.   glCallList(LEG_WHOLE);
  324.   glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
  325.   glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
  326.   glCallList(EYE_WHOLE);
  327.   glPopMatrix();
  328. }
  329.  
  330. enum {
  331.   MOD_DINO, MOD_SPHERE, MOD_CUBE, MOD_ICO
  332. };
  333.  
  334. static int currentModel = MOD_DINO;
  335.  
  336. static GLfloat blueMaterial[] = {0.0, 0.2, 1.0, 1.0},
  337.   redMaterial[] = {0.6, 0.1, 0.0, 1.0},
  338.   purpleMaterial[] = {0.3, 0.0, 0.3, 1.0},
  339.   greenMaterial[] = {1.0, 0.2, 0.0, 1.0};
  340.  
  341. static void
  342. drawModel(void)
  343. {
  344.   switch(currentModel) {
  345.   case MOD_DINO:
  346.     drawDinosaur();
  347.     break;
  348.   case MOD_SPHERE:
  349.     glMaterialfv(GL_FRONT, GL_DIFFUSE, blueMaterial);
  350.     glutSolidSphere(6.0, 15, 15);
  351.     break;
  352.   case MOD_CUBE:
  353.     glMaterialfv(GL_FRONT, GL_DIFFUSE, redMaterial);
  354.     glutSolidCube(6.0);
  355.     break;
  356.   case MOD_ICO:
  357.     glMaterialfv(GL_FRONT, GL_DIFFUSE, purpleMaterial);
  358.     glPushMatrix();
  359.       glEnable(GL_NORMALIZE);
  360.       glScalef(7.0, 7.0, 7.0);
  361.       glutSolidIcosahedron();
  362.       glDisable(GL_NORMALIZE);
  363.     glPopMatrix();
  364.     break;
  365.   }
  366. }
  367.  
  368. static void
  369. drawBox(GLfloat xsize, GLfloat ysize, GLfloat zsize)
  370. {
  371.   static GLfloat n[6][3] =
  372.   {
  373.     {-1.0, 0.0, 0.0},
  374.     {0.0, 1.0, 0.0},
  375.     {1.0, 0.0, 0.0},
  376.     {0.0, -1.0, 0.0},
  377.     {0.0, 0.0, 1.0},
  378.     {0.0, 0.0, -1.0}
  379.   };
  380.   static GLint faces[6][4] =
  381.   {
  382.     {0, 1, 2, 3},
  383.     {3, 2, 6, 7},
  384.     {7, 6, 5, 4},
  385.     {4, 5, 1, 0},
  386.     {5, 6, 2, 1},
  387.     {7, 4, 0, 3}
  388.   };
  389.   GLfloat v[8][3];
  390.   GLint i;
  391.  
  392.   v[0][0] = v[1][0] = v[2][0] = v[3][0] = -xsize / 2;
  393.   v[4][0] = v[5][0] = v[6][0] = v[7][0] = xsize / 2;
  394.   v[0][1] = v[1][1] = v[4][1] = v[5][1] = -ysize / 2;
  395.   v[2][1] = v[3][1] = v[6][1] = v[7][1] = ysize / 2;
  396.   v[0][2] = v[3][2] = v[4][2] = v[7][2] = -zsize / 2;
  397.   v[1][2] = v[2][2] = v[5][2] = v[6][2] = zsize / 2;
  398.  
  399.   for (i = 0; i < 6; i++) {
  400.     glBegin(GL_QUADS);
  401.     glNormal3fv(&n[i][0]);
  402.     glVertex3fv(&v[faces[i][0]][0]);
  403.     glVertex3fv(&v[faces[i][1]][0]);
  404.     glVertex3fv(&v[faces[i][2]][0]);
  405.     glVertex3fv(&v[faces[i][3]][0]);
  406.     glEnd();
  407.   }
  408. }
  409.  
  410. static void
  411. drawPillar(void)
  412. {
  413.   glEnable(GL_NORMALIZE);
  414.   glMaterialfv(GL_FRONT, GL_DIFFUSE, greenMaterial);
  415.   glPushMatrix();
  416.     glTranslatef(8.0, 4.01, 8.0);
  417.     drawBox(2.0, 8.0, 2.0);
  418.     glutSolidCube(2.0);
  419.   glPopMatrix();
  420.   glDisable(GL_NORMALIZE);
  421. }
  422.  
  423. static GLfloat floorVertices[4][3] = {
  424.   { -20.0, 0.0, 20.0 },
  425.   { 20.0, 0.0, 20.0 },
  426.   { 20.0, 0.0, -20.0 },
  427.   { -20.0, 0.0, -20.0 },
  428. };
  429.  
  430. /* Draw a floor (possibly textured). */
  431. static void
  432. drawFloor(void)
  433. {
  434.   glDisable(GL_LIGHTING);
  435.  
  436.   if (useTexture) {
  437.     glEnable(GL_TEXTURE_2D);
  438.   }
  439.  
  440.   glBegin(GL_QUADS);
  441.     glTexCoord2f(0.0, 0.0);
  442.     glVertex3fv(floorVertices[0]);
  443.     glTexCoord2f(0.0, 16.0);
  444.     glVertex3fv(floorVertices[1]);
  445.     glTexCoord2f(16.0, 16.0);
  446.     glVertex3fv(floorVertices[2]);
  447.     glTexCoord2f(16.0, 0.0);
  448.     glVertex3fv(floorVertices[3]);
  449.   glEnd();
  450.  
  451.   if (useTexture) {
  452.     glDisable(GL_TEXTURE_2D);
  453.   }
  454.  
  455.   glEnable(GL_LIGHTING);
  456. }
  457.  
  458. static GLfloat floorPlane[4];
  459. static GLfloat floorShadow[4][4];
  460.  
  461. static void
  462. redraw(void)
  463. {
  464.   int start, end;
  465.  
  466.   if (reportSpeed) {
  467.     start = glutGet(GLUT_ELAPSED_TIME);
  468.   }
  469.  
  470.   /* Clear; default stencil clears to zero. */
  471.   if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow) || (haloScale > 1.0)) {
  472.     glStencilMask(0xffffffff);
  473.     glClearStencil(0x4);
  474.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  475.   } else {
  476.     /* Avoid clearing stencil when not using it. */
  477.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  478.   }
  479.  
  480.   /* Reposition the light source. */
  481.   lightPosition[0] = 15*cos(lightAngle);
  482.   lightPosition[1] = lightHeight;
  483.   lightPosition[2] = 15*sin(lightAngle);
  484.   if (directionalLight) {
  485.     lightPosition[3] = 0.0;
  486.   } else {
  487.     lightPosition[3] = 1.0;
  488.   }
  489.  
  490.   shadowMatrix(floorShadow, floorPlane, lightPosition);
  491.  
  492.   glPushMatrix();
  493.     /* Perform scene rotations based on user mouse input. */
  494.     glRotatef(angle2, 1.0, 0.0, 0.0);
  495.     glRotatef(angle, 0.0, 1.0, 0.0);
  496.      
  497.     /* Tell GL new light source position. */
  498.     glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
  499.  
  500.     if (renderReflection) {
  501.       if (stencilReflection) {
  502.         /* We can eliminate the visual "artifact" of seeing the "flipped"
  503.          model underneath the floor by using stencil.  The idea is
  504.        draw the floor without color or depth update but so that 
  505.        a stencil value of one is where the floor will be.  Later when
  506.        rendering the model reflection, we will only update pixels
  507.        with a stencil value of 1 to make sure the reflection only
  508.        lives on the floor, not below the floor. */
  509.  
  510.         /* Don't update color or depth. */
  511.         glDisable(GL_DEPTH_TEST);
  512.         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  513.  
  514.         /* Draw 1 into the stencil buffer. */
  515.         glEnable(GL_STENCIL_TEST);
  516.         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  517.         glStencilFunc(GL_ALWAYS, 1, 0x1);
  518.     glStencilMask(0x1);
  519.  
  520.         /* Now render floor; floor pixels just get their stencil set to 1. */
  521.         drawFloor();
  522.  
  523.         /* Re-enable update of color and depth. */ 
  524.         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  525.         glEnable(GL_DEPTH_TEST);
  526.  
  527.         /* Now, only render where stencil is set to 1. */
  528.         glStencilFunc(GL_EQUAL, 1, 0x1);  /* draw if ==1 */
  529.         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  530.       }
  531.  
  532.       glPushMatrix();
  533.  
  534.         /* The critical reflection step: Reflect 3D model through the floor
  535.            (the Y=0 plane) to make a relection. */
  536.         glScalef(1.0, -1.0, 1.0);
  537.  
  538.     /* Reflect the light position. */
  539.         glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
  540.  
  541.         /* To avoid our normals getting reversed and hence botched lighting
  542.        on the reflection, turn on normalize.  */
  543.         glEnable(GL_NORMALIZE);
  544.         glCullFace(GL_FRONT);
  545.  
  546.         /* Draw the reflected model. */
  547.     glPushMatrix();
  548.       glTranslatef(0, 8.01, 0);
  549.           drawModel();
  550.     glPopMatrix();
  551.     drawPillar();
  552.  
  553.         /* Disable noramlize again and re-enable back face culling. */
  554.         glDisable(GL_NORMALIZE);
  555.         glCullFace(GL_BACK);
  556.  
  557.       glPopMatrix();
  558.  
  559.       /* Switch back to the unreflected light position. */
  560.       glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
  561.  
  562.       if (stencilReflection) {
  563.         glDisable(GL_STENCIL_TEST);
  564.       }
  565.     }
  566.  
  567.     /* Back face culling will get used to only draw either the top or the
  568.        bottom floor.  This let's us get a floor with two distinct
  569.        appearances.  The top floor surface is reflective and kind of red.
  570.        The bottom floor surface is not reflective and blue. */
  571.  
  572.     /* Draw "bottom" of floor in blue. */
  573.     glFrontFace(GL_CW);  /* Switch face orientation. */
  574.     glColor4f(0.1, 0.1, 0.7, 1.0);
  575.     drawFloor();
  576.     glFrontFace(GL_CCW);
  577.  
  578.     if (renderShadow && stencilShadow) {
  579.      /* Draw the floor with stencil value 2.  This helps us only 
  580.     draw the shadow once per floor pixel (and only on the
  581.         floor pixels). */
  582.       glEnable(GL_STENCIL_TEST);
  583.       glStencilFunc(GL_ALWAYS, 0x2, 0x2);
  584.       glStencilMask(0x2);
  585.       glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  586.     }
  587.  
  588.     /* Draw "top" of floor.  Use blending to blend in reflection. */
  589.     glEnable(GL_BLEND);
  590.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  591.     glColor4f(1.0, 1.0, 1.0, 0.3);
  592.     drawFloor();
  593.     glDisable(GL_BLEND);
  594.  
  595.     if (renderShadow && stencilShadow) {
  596.       glDisable(GL_STENCIL_TEST);
  597.     }
  598.  
  599.     if (renderDinosaur) {
  600.       drawPillar();
  601.  
  602.       if (haloScale > 1.0) {
  603.     /* If halo effect is enabled, draw the model with its stencil set to 6
  604.        (arbitary value); later, we'll make sure not to update pixels tagged
  605.        as 6. */
  606.         glEnable(GL_STENCIL_TEST);
  607.         glStencilFunc(GL_ALWAYS, 0x0, 0x4);
  608.         glStencilMask(0x4);
  609.         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  610.       } 
  611.  
  612.       /* Draw "actual" dinosaur (or other model), not its reflection. */
  613.       glPushMatrix();
  614.         glTranslatef(0, 8.01, 0);
  615.         drawModel();
  616.       glPopMatrix();
  617.     }
  618.  
  619.     /* Begin shadow render. */
  620.     if (renderShadow) {
  621.  
  622.       /* Render the projected shadow. */
  623.  
  624.       if (stencilShadow) {
  625.  
  626.         /* Now, only render where stencil is set above 5 (ie, 6 where
  627.        the top floor is).  Update stencil with 2 where the shadow
  628.        gets drawn so we don't redraw (and accidently reblend) the
  629.        shadow). */
  630.     glEnable(GL_STENCIL_TEST);
  631.         glStencilFunc(GL_NOTEQUAL, 0x0, 0x2);
  632.         glStencilMask(0x2);
  633.         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  634.       }
  635.  
  636.       /* To eliminate depth buffer artifacts, we use polygon offset
  637.      to raise the depth of the projected shadow slightly so
  638.      that it does not depth buffer alias with the floor. */
  639.       if (offsetShadow) {
  640.     switch (polygonOffsetVersion) {
  641.     case EXTENSION:
  642. #ifdef GL_EXT_polygon_offset
  643.       glEnable(GL_POLYGON_OFFSET_EXT);
  644.       break;
  645. #endif
  646. #ifdef GL_VERSION_1_1
  647.     case ONE_DOT_ONE:
  648.           glEnable(GL_POLYGON_OFFSET_FILL);
  649.       break;
  650. #endif
  651.     case MISSING:
  652.       /* Oh well. */
  653.       break;
  654.     }
  655.       }
  656.  
  657.       /* Render 50% black shadow color on top of whatever the
  658.          floor appareance is. */
  659.       glEnable(GL_BLEND);
  660.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  661.       glDisable(GL_LIGHTING);  /* Force the 50% black. */
  662.       glColor4f(0.0, 0.0, 0.0, 0.5);
  663.  
  664.       glPushMatrix();
  665.     /* Project the shadow. */
  666.         glMultMatrixf((GLfloat *) floorShadow);
  667.     glPushMatrix();
  668.           glTranslatef(0, 8.01, 0);
  669.           drawModel();
  670.     glPopMatrix();
  671.     drawPillar();
  672.       glPopMatrix();
  673.  
  674.       glDisable(GL_BLEND);
  675.       glEnable(GL_LIGHTING);
  676.  
  677.       if (offsetShadow) {
  678.     switch (polygonOffsetVersion) {
  679. #ifdef GL_EXT_polygon_offset
  680.     case EXTENSION:
  681.       glDisable(GL_POLYGON_OFFSET_EXT);
  682.       break;
  683. #endif
  684. #ifdef GL_VERSION_1_1
  685.     case ONE_DOT_ONE:
  686.           glDisable(GL_POLYGON_OFFSET_FILL);
  687.       break;
  688. #endif
  689.     case MISSING:
  690.       /* Oh well. */
  691.       break;
  692.     }
  693.       }
  694.       if (stencilShadow) {
  695.         glDisable(GL_STENCIL_TEST);
  696.       }
  697.     } /* End shadow render. */
  698.  
  699.     /* Begin light source location render. */
  700.     glPushMatrix();
  701.       glDisable(GL_LIGHTING);
  702.       glColor3f(1.0, 1.0, 0.0);
  703.       if (directionalLight) {
  704.         /* Draw an arrowhead. */
  705.         glDisable(GL_CULL_FACE);
  706.         glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
  707.         glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
  708.         glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
  709.         glBegin(GL_TRIANGLE_FAN);
  710.         glVertex3f(0, 0, 0);
  711.       glVertex3f(2, 1, 1);
  712.       glVertex3f(2, -1, 1);
  713.       glVertex3f(2, -1, -1);
  714.       glVertex3f(2, 1, -1);
  715.       glVertex3f(2, 1, 1);
  716.         glEnd();
  717.         /* Draw a white line from light direction. */
  718.         glColor3f(1.0, 1.0, 1.0);
  719.         glBegin(GL_LINES);
  720.       glVertex3f(0.1, 0, 0);
  721.       glVertex3f(5, 0, 0);
  722.         glEnd();
  723.         glEnable(GL_CULL_FACE);
  724.       } else {
  725.         /* Draw a yellow ball at the light source. */
  726.         glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
  727.         glutSolidSphere(1.0, 5, 5);
  728.       }
  729.       glEnable(GL_LIGHTING);
  730.     glPopMatrix();
  731.     /* End light source location render. */
  732.  
  733.     /* Add a halo effect around the 3D model. */
  734.     if (haloScale > 1.0) {
  735.  
  736.       glDisable(GL_LIGHTING);
  737.  
  738.       if (blendedHalo) {
  739.     /* If we are doing a nice blended halo, enable blending and
  740.        make sure we only blend a halo pixel once and that we do
  741.        not draw to pixels tagged as 6 (where the model is). */
  742.     glEnable(GL_BLEND);
  743.     glEnable(GL_STENCIL_TEST);
  744.     glColor4f(0.8, 0.8, 0.0, 0.3);  /* 30% sorta yellow. */
  745.         glStencilFunc(GL_EQUAL, 0x4, 0x4);
  746.         glStencilMask(0x4);
  747.         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
  748.       } else {
  749.     /* Be cheap; no blending.  Just draw yellow halo but not updating
  750.        pixels where the model is.  We don't update stencil at all. */
  751.         glDisable(GL_BLEND);
  752.     glEnable(GL_STENCIL_TEST);
  753.     glColor3f(0.5, 0.5, 0.0);  /* Half yellow. */
  754.         glStencilFunc(GL_EQUAL, 0x4, 0x4);
  755.         glStencilMask(0x4);
  756.         glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
  757.       }
  758.  
  759.       glPushMatrix();
  760.         glTranslatef(0, 8.01, 0);
  761.         glScalef(haloScale, haloScale, haloScale);
  762.         drawModel();
  763.       glPopMatrix();
  764.  
  765.       if (blendedHalo) {
  766.         glDisable(GL_BLEND);
  767.       }
  768.       glDisable(GL_STENCIL_TEST);
  769.       glEnable(GL_LIGHTING);
  770.     }
  771.     /* End halo effect render. */
  772.  
  773.   glPopMatrix();
  774.  
  775.   if (reportSpeed) {
  776.     glFinish();
  777.     end = glutGet(GLUT_ELAPSED_TIME);
  778.     printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
  779.   }
  780.  
  781.   glutSwapBuffers();
  782. }
  783.  
  784. /* ARGSUSED2 */
  785. static void
  786. mouse(int button, int state, int x, int y)
  787. {
  788.   if (button == GLUT_LEFT_BUTTON) {
  789.     if (state == GLUT_DOWN) {
  790.       moving = 1;
  791.       startx = x;
  792.       starty = y;
  793.     }
  794.     if (state == GLUT_UP) {
  795.       moving = 0;
  796.     }
  797.   }
  798.   if (button == GLUT_MIDDLE_BUTTON) {
  799.     if (state == GLUT_DOWN) {
  800.       lightMoving = 1;
  801.       lightStartX = x;
  802.       lightStartY = y;
  803.     }
  804.     if (state == GLUT_UP) {
  805.       lightMoving = 0;
  806.     }
  807.   }
  808. }
  809.  
  810. /* ARGSUSED1 */
  811. static void
  812. motion(int x, int y)
  813. {
  814.   if (moving) {
  815.     angle = angle + (x - startx);
  816.     angle2 = angle2 + (y - starty);
  817.     startx = x;
  818.     starty = y;
  819.     glutPostRedisplay();
  820.   }
  821.   if (lightMoving) {
  822.     lightAngle += (x - lightStartX)/40.0;
  823.     lightHeight += (lightStartY - y)/20.0;
  824.     lightStartX = x;
  825.     lightStartY = y;
  826.     glutPostRedisplay();
  827.   }
  828. }
  829.  
  830. static const float maxHalo[] = { 0.2, 0.35, 0.3, 0.5 };
  831.  
  832. /* Advance time varying state when idle callback registered. */
  833. static void
  834. idle(void)
  835. {
  836.   static float time = 0.0;
  837.  
  838.   if (animation) {
  839.     time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
  840.  
  841.     jump = 4.0 * fabs(sin(time)*0.8);
  842.     if (!lightMoving) {
  843.       lightAngle += 0.03;
  844.     }
  845.   }
  846.   if (haloMagic) {
  847.     haloTime += 0.1;
  848.     haloScale = 1.0 + maxHalo[currentModel] * sin(haloTime);
  849.     if (haloScale <= 1.0) {
  850.       haloMagic = 0;
  851.       if (!animation) {
  852.         glutIdleFunc(NULL);
  853.       }
  854.     }
  855.   }
  856.   glutPostRedisplay();
  857. }
  858.  
  859. enum {
  860.   M_NONE, M_BLENDED_HALO, M_SHOW_HALO, M_SWITCH_MODEL, M_MOTION, M_LIGHT,
  861.   M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
  862.   M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
  863.   M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
  864. };
  865.  
  866. static void
  867. controlLights(int value)
  868. {
  869.   switch (value) {
  870.   case M_NONE:
  871.     return;
  872.   case M_SWITCH_MODEL:
  873.     currentModel = (currentModel + 1) % 4;
  874.     break;
  875.   case M_SHOW_HALO:
  876.     haloScale = 1.0 + maxHalo[currentModel];
  877.     break;
  878.   case M_BLENDED_HALO:
  879.     blendedHalo = 1 - blendedHalo;
  880.     break;
  881.   case M_MOTION:
  882.     animation = 1 - animation;
  883.     if (animation || haloMagic) {
  884.       glutIdleFunc(idle);
  885.     } else {
  886.       glutIdleFunc(NULL);
  887.     }
  888.     break;
  889.   case M_LIGHT:
  890.     lightSwitch = !lightSwitch;
  891.     if (lightSwitch) {
  892.       glEnable(GL_LIGHT0);
  893.     } else {
  894.       glDisable(GL_LIGHT0);
  895.     }
  896.     break;
  897.   case M_TEXTURE:
  898.     useTexture = !useTexture;
  899.     break;
  900.   case M_SHADOWS:
  901.     renderShadow = 1 - renderShadow;
  902.     break;
  903.   case M_REFLECTION:
  904.     renderReflection = 1 - renderReflection;
  905.     break;
  906.   case M_DINOSAUR:
  907.     renderDinosaur = 1 - renderDinosaur;
  908.     break;
  909.   case M_STENCIL_REFLECTION:
  910.     stencilReflection = 1 - stencilReflection;
  911.     break;
  912.   case M_STENCIL_SHADOW:
  913.     stencilShadow = 1 - stencilShadow;
  914.     break;
  915.   case M_OFFSET_SHADOW:
  916.     offsetShadow = 1 - offsetShadow;
  917.     break;
  918.   case M_POSITIONAL:
  919.     directionalLight = 0;
  920.     break;
  921.   case M_DIRECTIONAL:
  922.     directionalLight = 1;
  923.     break;
  924.   case M_PERFORMANCE:
  925.     reportSpeed = 1 - reportSpeed;
  926.     break;
  927.   }
  928.   glutPostRedisplay();
  929. }
  930.  
  931. /* When not visible, stop animating.  Restart when visible again. */
  932. static void 
  933. visible(int vis)
  934. {
  935.   if (vis == GLUT_VISIBLE) {
  936.     if (animation || haloMagic)
  937.       glutIdleFunc(idle);
  938.   } else {
  939.     if (!animation && !haloMagic)
  940.       glutIdleFunc(NULL);
  941.   }
  942. }
  943.  
  944. /* Press any key to redraw; good when motion stopped and
  945.    performance reporting on. */
  946. /* ARGSUSED */
  947. static void
  948. key(unsigned char c, int x, int y)
  949. {
  950.   if (c == 27) {
  951.     exit(0);  /* IRIS GLism, Escape quits. */
  952.   }
  953.   if (c == ' ') {
  954.     haloMagic = 1;
  955.     haloTime = 0.0;
  956.     glutIdleFunc(idle);
  957.   }
  958.   glutPostRedisplay();
  959. }
  960.  
  961. /* Press any key to redraw; good when motion stopped and
  962.    performance reporting on. */
  963. /* ARGSUSED */
  964. static void
  965. special(int k, int x, int y)
  966. {
  967.   glutPostRedisplay();
  968. }
  969.  
  970. static int
  971. supportsOneDotOne(void)
  972. {
  973.   const char *version;
  974.   int major, minor;
  975.  
  976.   version = (char *) glGetString(GL_VERSION);
  977.   if (sscanf(version, "%d.%d", &major, &minor) == 2)
  978.     return major >= 1 && minor >= 1;
  979.   return 0;            /* OpenGL version string malformed! */
  980. }
  981.  
  982. int
  983. main(int argc, char **argv)
  984. {
  985.   int i;
  986.  
  987.   glutInit(&argc, argv);
  988.  
  989.   for (i=1; i<argc; i++) {
  990.     if (!strcmp("-linear", argv[i])) {
  991.       linearFiltering = 1;
  992.     } else if (!strcmp("-mipmap", argv[i])) {
  993.       useMipmaps = 1;
  994.     } else if (!strcmp("-ext", argv[i])) {
  995.       forceExtension = 1;
  996.     }
  997.   }
  998.  
  999.   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL | GLUT_MULTISAMPLE);
  1000.  
  1001. #if 1
  1002.   /* In GLUT 4.0, you'll be able to do this an be sure to
  1003.      get 2 bits of stencil if the machine has it for you. */
  1004.   glutInitDisplayString("samples stencil~3 rgb double depth");
  1005. #endif
  1006.  
  1007.   glutCreateWindow("OpenGL Halo Magic (hit Space)");
  1008.  
  1009.   if (glutGet(GLUT_WINDOW_STENCIL_SIZE) < 3) {
  1010.     printf("halomagic: Sorry, I need at least 3 bits of stencil.\n");
  1011.     exit(1);
  1012.   }
  1013.  
  1014.   /* Register GLUT callbacks. */
  1015.   glutDisplayFunc(redraw);
  1016.   glutMouseFunc(mouse);
  1017.   glutMotionFunc(motion);
  1018.   glutVisibilityFunc(visible);
  1019.   glutKeyboardFunc(key);
  1020.   glutSpecialFunc(special);
  1021.  
  1022.   glutCreateMenu(controlLights);
  1023.  
  1024.   glutAddMenuEntry("Toggle halo blending", M_BLENDED_HALO);
  1025.   glutAddMenuEntry("Show halo", M_SHOW_HALO);
  1026.   glutAddMenuEntry("Switch model", M_SWITCH_MODEL);
  1027.   glutAddMenuEntry("Toggle motion", M_MOTION);
  1028.   glutAddMenuEntry("-----------------------", M_NONE);
  1029.   glutAddMenuEntry("Toggle light", M_LIGHT);
  1030.   glutAddMenuEntry("Toggle texture", M_TEXTURE);
  1031.   glutAddMenuEntry("Toggle shadows", M_SHADOWS);
  1032.   glutAddMenuEntry("Toggle reflection", M_REFLECTION);
  1033.   glutAddMenuEntry("Toggle object", M_DINOSAUR);
  1034.   glutAddMenuEntry("-----------------------", M_NONE);
  1035.   glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
  1036.   glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
  1037.   glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
  1038.   glutAddMenuEntry("----------------------", M_NONE);
  1039.   glutAddMenuEntry("Positional light", M_POSITIONAL);
  1040.   glutAddMenuEntry("Directional light", M_DIRECTIONAL);
  1041.   glutAddMenuEntry("-----------------------", M_NONE);
  1042.   glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
  1043.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  1044.   makeDinosaur();
  1045.  
  1046. #ifdef GL_VERSION_1_1
  1047.   if (supportsOneDotOne() && !forceExtension) {
  1048.     polygonOffsetVersion = ONE_DOT_ONE;
  1049.     glPolygonOffset(-2.0, -1.0);
  1050.   } else
  1051. #endif
  1052.   {
  1053. #ifdef GL_EXT_polygon_offset
  1054.   /* check for the polygon offset extension */
  1055.   if (glutExtensionSupported("GL_EXT_polygon_offset")) {
  1056.     polygonOffsetVersion = EXTENSION;
  1057.     glPolygonOffsetEXT(-0.1, -0.002);
  1058.   } else 
  1059. #endif
  1060.     {
  1061.       polygonOffsetVersion = MISSING;
  1062.       printf("\ndinoshine: Missing polygon offset.\n");
  1063.       printf("           Expect shadow depth aliasing artifacts.\n\n");
  1064.     }
  1065.   }
  1066.  
  1067.   glEnable(GL_CULL_FACE);
  1068.   glEnable(GL_DEPTH_TEST);
  1069.   glLineWidth(3.0);
  1070.  
  1071.   glMatrixMode(GL_PROJECTION);
  1072.   gluPerspective( /* field of view in degree */ 40.0,
  1073.   /* aspect ratio */ 1.0,
  1074.     /* Z near */ 20.0, /* Z far */ 100.0);
  1075.   glMatrixMode(GL_MODELVIEW);
  1076.   gluLookAt(0.0, 8.0, 60.0,  /* eye is at (0,0,30) */
  1077.     0.0, 8.0, 0.0,      /* center is at (0,0,0) */
  1078.     0.0, 1.0, 0.);      /* up is in postivie Y direction */
  1079.  
  1080.   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
  1081.   glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
  1082.   glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
  1083.   glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
  1084.   glEnable(GL_LIGHT0);
  1085.   glEnable(GL_LIGHTING);
  1086.  
  1087.   makeFloorTexture();
  1088.  
  1089.   /* Setup floor plane for projected shadow calculations. */
  1090.   findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
  1091.  
  1092.   glutMainLoop();
  1093.   return 0;             /* ANSI C requires main to return int. */
  1094. }
  1095.